package com.pai4j.zwiki.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * ZIP文件处理服务
 * 负责处理用户上传的ZIP格式项目文件
 *
 * @author pai4j
 * @since 2025-10-12
 */
@Slf4j
@Service
public class ZipFileService {

    @Value("${zwiki.workspace.base-path:/tmp/zwiki-workspace}")
    private String workspaceBasePath;

    @Value("${zwiki.workspace.max-size-mb:500}")
    private int maxSizeMb;

    /**
     * 解压ZIP文件到工作目录
     *
     * @param zipFile 上传的ZIP文件
     * @param taskId  任务ID
     * @return 解压后的目录路径
     */
    public String extractZipFile(MultipartFile zipFile, String taskId) {
        log.info("开始解压ZIP文件, 任务ID: {}, 文件大小: {} bytes", taskId, zipFile.getSize());

        // 检查文件大小
        long maxSizeBytes = maxSizeMb * 1024L * 1024L;
        if (zipFile.getSize() > maxSizeBytes) {
            throw new RuntimeException("文件大小超过限制: " + maxSizeMb + "MB");
        }

        try {
            // 创建工作目录
            Path workspacePath = Paths.get(workspaceBasePath, taskId);
            Files.createDirectories(workspacePath);

            // 解压文件（添加 Zip Slip 攻击防护）
            try (InputStream is = zipFile.getInputStream();
                 ZipArchiveInputStream zis = new ZipArchiveInputStream(is, "UTF-8")) {

                ZipArchiveEntry entry;
                while ((entry = zis.getNextZipEntry()) != null) {
                    // 防止 Zip Slip 攻击：验证解压路径不会逃逸到工作目录之外
                    String entryName = entry.getName();
                    if (entryName.contains("..") || entryName.startsWith("/") || entryName.contains("\\..")) {
                        log.warn("检测到潜在的 Zip Slip 攻击，跳过条目: {}", entryName);
                        continue;
                    }
                    
                    Path resolvedPath = workspacePath.resolve(entryName).normalize();
                    if (!resolvedPath.startsWith(workspacePath)) {
                        log.warn("路径验证失败，跳过条目: {}", entryName);
                        continue;
                    }
                    
                    if (entry.isDirectory()) {
                        // 创建目录
                        Files.createDirectories(resolvedPath);
                    } else {
                        // 提取文件
                        Path parentDir = resolvedPath.getParent();
                        if (parentDir != null) {
                            Files.createDirectories(parentDir);
                        }

                        try (OutputStream os = Files.newOutputStream(resolvedPath)) {
                            byte[] buffer = new byte[8192];
                            int len;
                            while ((len = zis.read(buffer)) > 0) {
                                os.write(buffer, 0, len);
                            }
                        }
                    }
                }
            }

            log.info("ZIP文件解压完成: {}", workspacePath);
            return workspacePath.toString();

        } catch (IOException e) {
            log.error("解压ZIP文件失败", e);
            throw new RuntimeException("解压ZIP文件失败: " + e.getMessage(), e);
        }
    }

    /**
     * 验证ZIP文件
     */
    public boolean isValidZipFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return false;
        }

        String filename = file.getOriginalFilename();
        if (filename == null) {
            return false;
        }

        return filename.toLowerCase().endsWith(".zip");
    }

    /**
     * 从ZIP文件名提取项目名称
     */
    public String extractProjectName(MultipartFile file) {
        String filename = file.getOriginalFilename();
        if (filename == null) {
            return "unknown";
        }
        return filename.replace(".zip", "");
    }
}

